using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace SyncUdp
{
    using SyncUdp.Interface;
    using System;

    /// <summary>
    /// 事件中心的接口
    /// </summary>
    public interface IEventCenter : ISingleton
    {
        /// <summary>
        /// 用于下一帧分发事件
        /// </summary>
        public void Update();

        public void RegisterEvent<T>(Action<T> action) where T : IEventArgs;

        public void UnregisterEvent<T>() where T : IEventArgs;

        public void SendEvent<T>(T eventArgs) where T : IEventArgs;

        /// <summary>
        /// 下一帧分发事件，这么做可以让执行事件的线程转换为主线程
        /// </summary>
        public void SendEventOnNextFrame<T>(T eventArgs) where T : IEventArgs;
    }

    /// <summary>
    /// 事件中心
    /// </summary>
    public class EventCenter : IEventCenter
    {
        #region 辅助事件类及其接口
        interface ISimpleEvent { }

        class SimpleEvent<T> : ISimpleEvent 
            where T : IEventArgs
        {
            event Action<T> _actions;
            
            public void Register(Action<T> action)
            {
                _actions += action;
            }

            public void UnRegister(Action<T> action)
            {
                _actions -= action;
            }

            public void Invoke(T eventArgs)
            {
                _actions?.Invoke(eventArgs);
            }
        }
        #endregion

        Dictionary<Type, ISimpleEvent> _eventsDic;

        LinkedList<Action> _delayedEvnets;

        #region 接口实现
        public ISingleton Instance => this;

        public bool IsInited { get; private set; }

        public void Init()
        {
            if (IsInited) return;
            _eventsDic = new Dictionary<Type, ISimpleEvent>();
            _delayedEvnets = new LinkedList<Action>();
            IsInited = true;
        }

        public void Update()
        {
            if (_delayedEvnets.Count == 0)
                return;
            // 之所以用锁，而不是并发集合，
            // 是因为想要事件在该帧全部分发，防止还没分发完而又新添加一个
            lock (_delayedEvnets)
            {
                var node = _delayedEvnets.First;
                while (node != null)
                {
                    node.Value.Invoke();
                    var t = node;
                    node = node.Next;
                    _delayedEvnets.Remove(t);
                }
            }
        }

        public void RegisterEvent<T>(Action<T> action) where T : IEventArgs
        {
            var t = typeof(T);
            if (_eventsDic.ContainsKey(t))
            {
                var simpleEvent = _eventsDic[t] as SimpleEvent<T>;
                simpleEvent.Register(action);
            }
            else
            {
                var simpleEvent = new SimpleEvent<T>();
                simpleEvent.Register(action);
                _eventsDic.Add(t, simpleEvent);
            }
        }

        public void UnregisterEvent<T>() where T : IEventArgs
        {
            var t = typeof(T);
            if (_eventsDic.ContainsKey(t))
                _eventsDic.Remove(t);
        }

        public void SendEvent<T>(T eventArgs) where T : IEventArgs
        {
            var t = typeof(T);
            if (_eventsDic.TryGetValue(t, out var simple))
            {
                var simpleEvent = simple as SimpleEvent<T>;
                simpleEvent?.Invoke(eventArgs);
            }
        }

        public void SendEventOnNextFrame<T>(T eventArgs) where T : IEventArgs
        {
            lock (_delayedEvnets)
            {
                var simple = _eventsDic[typeof(T)] as SimpleEvent<T>;
                _delayedEvnets.AddLast(() => simple?.Invoke(eventArgs));
            }
        }
        #endregion
    }
}